package com.minivision.fdiot.listener;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import com.minivision.fdiot.common.Action;
import com.minivision.fdiot.domain.FaceData;
import com.minivision.fdiot.domain.FaceSetAction;
import com.minivision.fdiot.domain.FaceSetData;
import com.minivision.fdiot.entity.Device;
import com.minivision.fdiot.entity.FaceSet;
import com.minivision.fdiot.event.FaceSetUpdateEvent;
import com.minivision.fdiot.event.FaceUpdateEvent;
import com.minivision.fdiot.mqtt.handler.CommonSender;
import com.minivision.fdiot.service.DeviceService;
import com.minivision.fdiot.service.FaceSetService;

import lombok.extern.slf4j.Slf4j;

/**
 * 人脸库和人脸操作事件监听器
 * @author hughzhao
 * @2018年3月1日
 */
@Component
@Slf4j
public class FaceOperationListener {

  @Autowired
  private DeviceService deviceService;
  
  @Autowired
  private FaceSetService faceSetService;
  
  @Autowired
  private CommonSender sender;
  
  @Autowired
  @Qualifier("mqttSenderWorker")
  private ExecutorService mqttSenderWorker;
  
  /**
   * 向设备发送人脸库操作消息
   * @param appKey
   * @param action
   */
  private void refreshFaceSet2Device(String appKey, FaceSetAction action) {
    try {
      List<Device> devices = deviceService.findByAppKey(appKey);
      List<FaceSet> facesetList = faceSetService.findByAppKey(appKey);
      if (CollectionUtils.isEmpty(devices)) {
        log.warn("no devices to push, app:{}", appKey);
        return;
      }
      if (CollectionUtils.isEmpty(facesetList)) {
        log.warn("no facesets to push, app:{}", appKey);
        return;
      }
      List<FaceSetData> faceSetDatas = new ArrayList<>();
      facesetList.stream().forEach((faceset) -> {
        FaceSetData data = new FaceSetData();
        data.setFacesetToken(faceset.getFacesetToken());
        data.setDisplayName(faceset.getDisplayName());
        data.setCapacity(faceset.getCapacity());
        data.setPriority(faceset.getPriority());
        data.setThreshold(faceset.getThreshold());
        faceSetDatas.add(data);
      });
      
      if (CollectionUtils.isEmpty(faceSetDatas)) {
        log.warn("no faceset data to push, data:{}", faceSetDatas);
        return;
      }
      
      mqttSenderWorker.execute(() -> {
        for (Device device : devices) {
          try {
            sender.refreshFaceSet(device.getSn(), device.getModel(), faceSetDatas, action);
          } catch (Exception e) {
            log.error("refreshFaceSet to device[{}] failed", device.getSn());
            log.error("MQTT Exception", e);
          }
        }
      });
    } catch (Exception e) {
      log.error("push faceset update to device exception", e);
    }
  }
  
  /**
   * 向设备发送人脸操作消息
   * @param action
   * @param appKey
   * @param facesetToken
   * @param data
   */
  private void refreshFace2Device(Action action, String appKey, String facesetToken, List<FaceData> data) {
    if (CollectionUtils.isEmpty(data)) {
      log.warn("no face data to push, app:{}, faceset:{}, data:{}", appKey, facesetToken, data);
      return;
    }
    
    List<Device> devices = null;
    try {
      devices = deviceService.findByAppKey(appKey);
    } catch (Exception e) {
      log.error("find devices failed, appKey:" + appKey, e);
    }
    
    try {
      if (CollectionUtils.isEmpty(devices)) {
        log.warn("no devices found, app:{}", appKey);
        devices = deviceService.findByFaceSetToken(facesetToken);
      } else {
        devices = deviceService.findByAppKeyAndFaceSetToken(appKey, facesetToken);
      }
      
      if (CollectionUtils.isEmpty(devices)) {
        log.warn("no devices to push, app:{}, facesetToken:{}", appKey, facesetToken);
        return;
      }
      
      List<Device> pushDevices = devices;
      mqttSenderWorker.execute(() -> {
        for (Device device : pushDevices) {
          try {
            sender.refreshFace(action, device.getSn(), device.getModel(), data);
          } catch (Exception e) {
            log.error("refreshFace to device[{}] failed", device.getSn());
            log.error("MQTT Exception", e);
          }
        }
      });
    } catch (Exception e) {
      log.error("push face update to device exception", e);
    }
  }
  
  /**
   * 监听人脸库操作事件
   * @param event
   */
  @EventListener
  public void facesetUpdate(FaceSetUpdateEvent event) {
      log.info("Listening FaceSet Update : " + event.getFaceset().getFacesetToken());
      refreshFaceSet2Device(event.getAppKey(), event.getFaceset());
  }
  
  /**
   * 监听人脸操作事件
   * @param event
   */
  @EventListener
  public void faceUpdate(FaceUpdateEvent event) {
      log.info("Listening Face Update : " + event.getFaces().stream().map(f -> f.getToken()).collect(Collectors.toList()));
      refreshFace2Device(event.getAction(), event.getAppKey(), event.getFacesetToken(), event.getFaces());
  }
  
}
